package com.qiye.livevideo.utils;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.auth.DefaultCredentials;
import com.aliyun.oss.model.*;
import com.qiye.livevideo.common.RetConstant;
import com.qiye.livevideo.model.AliCloudOssInfo;
import com.qiye.livevideo.service.AliCloudOssInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created on 2019/5/1.
 *
 * @author meishijun
 */
@Component
public class AliyunOSSUtil {
    private static final Logger logger = LoggerFactory.getLogger(AliyunOSSUtil.class);
    @Autowired
    private  AliCloudOssInfoService aliCloudOssInfoService;
    private static AliyunOSSUtil aliyunOssUtil;
    @PostConstruct
    public void init() {
        aliyunOssUtil = this;
        aliyunOssUtil.aliCloudOssInfoService = this.aliCloudOssInfoService;
    }
    /**
     * 获取阿里云oss配置信息
     * @param accessKeyId
     * @return
     */
    public static AliCloudOssInfo getOssInfoByaccessKeyId(String accessKeyId){
         if(accessKeyId==null || accessKeyId.isEmpty()){
             accessKeyId = RetConstant.DEFAULT_ACCESS_KEY_ID;
         }
         AliCloudOssInfo aliCloudOssInfo = aliyunOssUtil.aliCloudOssInfoService.selectByaccessKeyId(accessKeyId);
         if(aliCloudOssInfo!=null){
             return aliCloudOssInfo;
         }else {
             return  new AliCloudOssInfo();
         }
    }
    /**
     * 获取阿里云OSS客户端对象
     *
     * @return ossClient
     */
    public static OSSClient getOSSClient(AliCloudOssInfo aliCloudOssInfo) {
        DefaultCredentials defaultCredentials = new DefaultCredentials(aliCloudOssInfo.getAccessKeyId(), aliCloudOssInfo.getAccessKeySeret());
        // 创建ClientConfiguration实例，按照您的需要修改默认参数。
        DefaultCredentialProvider credentialsProvider = new DefaultCredentialProvider(defaultCredentials);
        return  new OSSClient(aliCloudOssInfo.getOssRegionPoint(), credentialsProvider, new ClientConfiguration());
    }
    /**
     * 创建存储空间
     *
     * @param ossClient
     *            OSS连接
     * @param bucketName
     *            存储空间
     * @return
     */
    public static String createBucketName(OSSClient ossClient, String bucketName) {
        // 存储空间
        final String bucketNames = bucketName;
        if (!ossClient.doesBucketExist(bucketName)) {
            // 创建存储空间
            Bucket bucket = ossClient.createBucket(bucketName);
            logger.info("创建存储空间成功");
            return bucket.getName();
        }
        return bucketNames;
    }

    /**
     * 删除存储空间buckName
     *
     * @param ossClient
     *            oss对象
     * @param bucketName
     *            存储空间
     */
    public static void deleteBucket(OSSClient ossClient, String bucketName) {
        ossClient.deleteBucket(bucketName);
        logger.info("删除" + bucketName + "Bucket成功");
    }

    /**
     * 创建模拟文件夹
     *
     * @param ossClient
     *            oss连接
     * @param bucketName
     *            存储空间
     * @param folder
     *            模拟文件夹名如"qj_nanjing/"
     * @return 文件夹名
     */
    public static String createFolder(OSSClient ossClient, String bucketName, String folder) {
        // 文件夹名
        final String keySuffixWithSlash = folder;
        // 判断文件夹是否存在，不存在则创建
        if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) {
            // 创建文件夹
            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));
            logger.info("创建文件夹成功");
            // 得到文件夹名
            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);
            String fileDir = object.getKey();
            return fileDir;
        }
        return keySuffixWithSlash;
    }

    /**
     * 根据key删除OSS服务器上的文件

     * @param key Bucket下的文件的路径名+文件名 如："upload/cake.jpg"
     */
    public static List<String> deleteFile(String key,String accessKeyId) {
        AliCloudOssInfo aliCloudOssInfo = getOssInfoByaccessKeyId(accessKeyId);
        OSSClient ossClient = AliyunOSSUtil.getOSSClient(aliCloudOssInfo);
        // 删除文件。
        List<String> keys = new ArrayList<String>();
        ossClient.deleteObject(aliCloudOssInfo.getOssBucketName(), key);
        // 关闭OSSClient。
        ossClient.shutdown();
        List<String> deletedObjects = new ArrayList<>();
        logger.info("删除" + aliCloudOssInfo.getOssBucketName() + "下的文件    /"+ key + "成功");
        return deletedObjects;
    }

    /**
     * 上传图片至OSS 文件流
     * @param file
     *  存储空间
     * @return String 返回的唯一MD5数字签名
     */
    public static String generalUploadObject(File file,String accessKeyId) {
        String objectName = "";
        try {
            AliCloudOssInfo aliCloudOssInfo = getOssInfoByaccessKeyId(accessKeyId);
            OSSClient ossClient = AliyunOSSUtil.getOSSClient(aliCloudOssInfo);
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            // 以输入流的形式上传文件
            InputStream is = new FileInputStream(file);
            // 文件名
            String[] fileArg = file.getName().split("\\.");
            objectName = aliCloudOssInfo.getOssOrdinaryFile() + "/" + format.format(new Date()) + "/" + UUID.randomUUID().toString().replace("-", "") + "." + fileArg[1];
            ossClient.putObject(aliCloudOssInfo.getOssBucketName(), objectName, is);
            ossClient.shutdown();
            logger.info("上传到路径" + objectName);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
        }
        return objectName;
    }


    /**
     * 获得url链接
     *
     * @return
     */
    public static String getUrl( String fileName,String accessKeyId) {
        AliCloudOssInfo aliCloudOssInfo = getOssInfoByaccessKeyId(accessKeyId);
        OSSClient ossClient = AliyunOSSUtil.getOSSClient(aliCloudOssInfo);
        // 设置URL过期时间为10年 3600l* 1000*24*365*10
        Date expiration = new Date(System.currentTimeMillis() + 3600L * 1000 * 24 * 365 * 10);
        // 生成URL
        URL url = ossClient.generatePresignedUrl(aliCloudOssInfo.getOssBucketName(), fileName, expiration);
        if (url != null) {
            return url.toString();
        }
        return "获网址路径出错";
    }


    /**
     * 分片上传
     * @param file
     */
    public static String multipartUpload(File file,String accessKeyId){
        String objectName = "";
        try {
            AliCloudOssInfo aliCloudOssInfo = getOssInfoByaccessKeyId(accessKeyId);
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            // 文件名
            String[] fileArg = file.getName().split("\\.");
            objectName = aliCloudOssInfo.getOssMultipartFile() + "/" + format.format(new Date()) + "/" + UUID.randomUUID().toString().replace("-", "") + "." + fileArg[1];
            // 创建OSSClient实例。
            OSSClient ossClient = getOSSClient(aliCloudOssInfo);

            /* 步骤1：初始化一个分片上传事件。
             */
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(aliCloudOssInfo.getOssBucketName(), objectName);
            InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(request);
            // 返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等。
            String uploadId = result.getUploadId();

            /* 步骤2：上传分片。
             */
            // partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
            List<PartETag> partETags = new ArrayList<PartETag>();
            // 计算文件有多少个分片。
            // 1MB
            final long partSize = 1 * 1024 * 1024L;
            final File sampleFile = file;
            long fileLength = sampleFile.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }


            // 遍历分片上传。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                InputStream instream = new FileInputStream(sampleFile);
                // 跳过已经上传的分片。
                instream.skip(startPos);
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(aliCloudOssInfo.getOssBucketName());
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                uploadPartRequest.setInputStream(instream);
                // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
                uploadPartRequest.setPartNumber(i + 1);
                // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }


            /* 步骤3：完成分片上传。
             */
            // 排序。partETags必须按分片号升序排列。
            Collections.sort(partETags, new Comparator<PartETag>() {
                @Override
                public int compare(PartETag p1, PartETag p2) {
                    return p1.getPartNumber() - p2.getPartNumber();
                }
            });
            // 在执行该操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(aliCloudOssInfo.getOssBucketName(), objectName, uploadId, partETags);
            ossClient.completeMultipartUpload(completeMultipartUploadRequest);

            // 关闭OSSClient。
            ossClient.shutdown();
            logger.info("上传到路径" + objectName);
        } catch (Exception e) {
            logger.info("上传阿里云OSS服务器异常:" + e.getMessage(),e);
        }
        return  objectName;
    }

}
